#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
using namespace std;
enum class status
{
    NEW,          // 新建状态，就是单纯的连接
    CONNECTING,   // 正在连接，仅仅方便查询conn状态
    CONNECTED,    // 连接或者重连成功
    DISCONNECTED, // 重连失败
    CLOSED        // 连接失败，经历重连，无法连接

};

class client_connection
{
public:
    client_connection(const string &server_ip, uint16_t server_port)
        : _server_ip(server_ip),
          _server_port(server_port),
          _sockfd(-1),
          _recon_interval(1),
          _max_retries(6),
          _status(status::NEW)
    {
    }
    void client_connect()
    {
        //创建套接字
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            cerr << "socket error" << endl;
            exit(1);
        }
        // 2. 要不要bind？必须要有Ip 和Port, 需要bind，但是不需要用
        // 户显示的bind，client 系统随机端口
        // 发起连接的时候，client 会被OS 自动进行本地绑定
        // 2. connect
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_server_port);
        // p:process(进程), n(网络) -- 不太准确，但是好记忆
        inet_pton(AF_INET, _server_ip.c_str(), &server.sin_addr);
        // 1. 字符串ip->4 字节IP 2. 网络序列
        int n = connect(_sockfd, (struct sockaddr *)&server,
                        sizeof(server)); // 自动进行bind 哦！
        if (n < 0)
        {
            disconnect();                   // 恢复_sockfd 的默认
                                            // 值，是连接没有成功，不代表sockfd 创建没有成功
            _status = status::DISCONNECTED; // 没有连接成功
            return;
        }
    }
    int sockfd()
    {
        return _sockfd;
    }
    void reconnect()
    {
        _status = status::CONNECTING;
        int cnt = 0;
        while (true)
        {
            client_connect();                 // 重连
            if (_status == status::CONNECTED) // 连接成功
            {
                return;
            }
            cnt++;
            if (cnt > _max_retries)
            {
                _status = status::CLOSED;
                cout << "连接失败，请检查网络..." << endl;
                break;
            }
            cout << "重连次数：" << cnt << ", 最大上限：" << _max_retries << endl;
            sleep(_recon_interval); // 连接时间间隔
        }
    }
    void disconnect()
    {
        if (_sockfd != -1) // 已经创建文件描述符
        {
            close(_sockfd); // 关闭文件描述符
            _status = status::CLOSED;
            _sockfd = -1;
        }
    }
    status get_status()
    {
        return _status;
    }
    void process()
    {
        while (true)
        {
            string inbuffer;
            cout << "Please enter:";
            getline(cin, inbuffer); // 从输入流中读取一行到buffer中
            if (inbuffer.empty())
                continue;
            ssize_t n = write(_sockfd, inbuffer.c_str(), inbuffer.size()); // 向文件描述符_sockfd中写入inbuffer的内容
            if (n > 0)
            {
                // 写入成功
                char buffer[1024];
                ssize_t m = read(_sockfd, buffer, sizeof(buffer));
                if (n > 0)
                {
                    // 读取成功
                    buffer[m] = 0;
                    cout << "echo massage-> " << buffer << endl;
                }
                else if (m == 0) // read() 函数返回0。这意味着没有更多的数据可以读取,server端掉线了
                {
                    _status = status::DISCONNECTED;
                    break;
                }
                else
                {
                    cout << "read m: " << m << "errno: " << errno << "errno stirng:" << strerror(errno) << endl;
                    _status = status::CLOSED;
                    break;
                }
            }
            else
            {
                cout << "write n : " << n << "errno: " << errno << "errno string " << strerror(errno) << std::endl;
                _status = status::CLOSED;
                break;
            }
        }
    }
    ~client_connection()
    {
    }

private:
    string _server_ip;     // ip
    uint16_t _server_port; // port
    int _sockfd;           // 文件描述符
    int _recon_interval;   // 重新连接时间间隔
    int _max_retries;      // 重新连接次数
    status _status;        // 连接状态
};
class tcp_client
{
public:
    tcp_client(const string &server_ip, uint16_t server_port)
        : _client(server_ip, server_port)
    {
    }
    void execute()
    {
        while (true)
        {
            switch (_client.get_status())
            {
            case status::NEW:
                _client.client_connect();
                break;
            case status::CONNECTED:
                cout << "连接成功，开始进行通信" << endl;
                _client.process();
                break;
            case status::DISCONNECTED:
                cout << "连接失败或对方掉线，开始重连" << endl;
                _client.reconnect();
                break;
            case status::CLOSED:
                _client.disconnect();
                cout << "连接失败，退出" << endl;
                return;
            default:
                break;
            }
        }
    }
    ~tcp_client()
    {
    }

private:
    client_connection _client; // client_connection类对象
};

void Usage(const string &str)
{
    cout << "usage:" << str << " serverip serverport" << endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string server_ip = argv[1];
    uint16_t server_port = stoi(argv[2]);
    tcp_client client(server_ip, server_port); // 构造一个tcpclient类对象
    client.execute();

    return 0;
}